#include "mesh_loader.h"
#include "ext/tiny_obj_loader.h"
#include "ext/tiny_gltf.h"


namespace vortex
{
    int MeshLoader::load_obj(const std::string& model_path, SampleMesh& mesh)
    {
        // clear mesh
        mesh.positions.clear();
        mesh.normal.clear();
        mesh.texcoords.clear();

        tinyobj::attrib_t attrib;
        std::vector<tinyobj::shape_t> shapes;
        std::vector<tinyobj::material_t> materials;
        
        std::string warn, err;

        bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, model_path.c_str());

        if (!warn.empty()) {
            std::cout << "WARN: " << warn << std::endl;
        }

        if (!err.empty()) {
            std::cerr << "ERR: " << err << std::endl;
        }

        if (!ret) {
            std::cerr << "Failed to load OBJ file." << std::endl;
            return -1;
        }

        std::cout << "Loaded " << shapes.size() << " shapes." << std::endl;
        std::cout << "Loaded " << materials.size() << " materials." << std::endl;

        glm::vec3 pos;
        glm::vec2 texcoord;
        glm::vec3 norm;

        for (size_t s = 0; s < shapes.size(); s++) {
            std::cout << "Shape " << s << " has " << shapes[s].mesh.indices.size() << " indices." << std::endl;

            for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) {
                int fv = shapes[s].mesh.num_face_vertices[f];

                for (int v = 0; v < fv; v++) {
                    tinyobj::index_t idx = shapes[s].mesh.indices[f * fv + v];

                    tinyobj::real_t vx = attrib.vertices[3 * idx.vertex_index + 0];
                    tinyobj::real_t vy = attrib.vertices[3 * idx.vertex_index + 1];
                    tinyobj::real_t vz = attrib.vertices[3 * idx.vertex_index + 2];
                    // std::cout << "Vertex: (" << vx << ", " << vy << ", " << vz << ")" << std::endl;
                    
                    pos.x = vx;
                    pos.y = vy;
                    pos.z = vz;
                    mesh.positions.push_back(pos);

                    tinyobj::real_t tx = 0.0f;
                    tinyobj::real_t ty = 0.0f;
                    if (idx.texcoord_index != -1) {
                        tx = attrib.texcoords[2 * idx.texcoord_index + 0];
                        ty = attrib.texcoords[2 * idx.texcoord_index + 1];
                    }
                    texcoord.x = tx;
                    texcoord.y = ty;
                    mesh.texcoords.push_back(texcoord);

                    tinyobj::real_t nx = 0.0f;
                    tinyobj::real_t ny = 0.0f;
                    tinyobj::real_t nz = 0.0f;
                    if (idx.normal_index != -1) {
                        nx = attrib.normals[3 * idx.normal_index + 0];
                        ny = attrib.normals[3 * idx.normal_index + 1];
                        nz = attrib.normals[3 * idx.normal_index + 2];
                    }
                    norm.x = nx;
                    norm.y = ny;
                    norm.z = nz;
                    mesh.normal.push_back(norm);
                }
            }
        }

        return 0;
    }

    int MeshLoader::load_gltf(const std::string& model_path, bool is_glb)
    {
        tinygltf::Model model;
        tinygltf::TinyGLTF loader;
        std::string err;
        std::string warn;

        bool load_ok;
        if (is_glb) {
            load_ok = loader.LoadBinaryFromFile(&model, &err, &warn, model_path);
        }
        else {
            load_ok = loader.LoadASCIIFromFile(&model, &err, &warn, model_path);
        }

        if (!warn.empty()) {
            std::cerr << "Warning: " << warn << std::endl;
        }

        if (!err.empty()) {
            std::cerr << "Error: " << err << std::endl;
        }

        if (!load_ok) {
            std::cerr << "Failed to load glTF file." << std::endl;
            return -1;
        }
        
        return 0;
    }
}
